home *** CD-ROM | disk | FTP | other *** search
/ The X-Philes (2nd Revision) / The X-Philes Number 1 (1995).iso / xphiles / hp48hor2 / tasc.c < prev    next >
C/C++ Source or Header  |  1992-08-18  |  12KB  |  404 lines

  1. /* Tasc:  HP48-binary <-> HP48-ASC.
  2.    Copyright 1992 by Jonathan T. Higa.
  3.    Distribute freely.
  4.    You may make modifications to suit your needs; simply document the changes.
  5.    
  6.    Dec 1991 to Mar 1992: parses hp48 binary correctly,
  7.       determines type of source file, handles stdin & stdout
  8.    12 June 1992: v2.50 -- auto name completion
  9.    19 June 1992: v2.51 -- bug fix for names < 4 chars; added @ comments
  10.    22 June 1992: v2.52 -- generalized report syntax
  11. */
  12.  
  13. #include <ctype.h>
  14. #include <limits.h>
  15. #include <stdio.h>
  16. #include <stdlib.h>
  17. #include <string.h>
  18.  
  19. #define ATOB 1
  20. #define BTOA 2
  21.  
  22. typedef unsigned long ulong;
  23.  
  24. const char ASCSUF[]=".asc"; /* ASC file suffix */
  25. const char VERSION[]="2.52";          /* TASC version */
  26. const char *ROMFMT="ROM Revision: %c\n";
  27.    /* ROM Revision report format: use %c */
  28. const char *BYTESFMT="BYTES: #%lXh %ld%s\n";
  29. const char *HALFFMT=".5";
  30.    /* BYTES report: use long for checksum, long for integral bytes,
  31.       and string for half-byte format string */
  32. int verb=1;                 /* verbosity flag */
  33. char rom='E';               /* default rom revision letter */
  34.  
  35. void *alloc(size_t n);
  36. int stricmp(const char *s1, const char *s2);
  37. ulong popbitq(ulong *buf, int *bsize, int nbits);
  38. ulong pushbitq(ulong *buf, int *bsize, int nbits, ulong bits);
  39. int bintoasc(FILE *fbin, FILE *fasc);
  40. int translate(const char *fsrc, const char *fdest, int mode);
  41.  
  42. #define calc_crc(crc, hex) (crc=(crc>>4)^(((crc^(hex))&0xF)*0x1081))
  43. /* Recalculates the Cyclic Redundancy Check value based on the old CRC
  44.    value crc and a new nibble hex.
  45.    Note:  crc should be an unsigned lvalue initialized to 0. */
  46.  
  47. #define lowbits(n) ((1uL << (n)) - 1uL)
  48. /* Creates an unsigned long with only the low n bits set. */
  49.  
  50. void *alloc(size_t n)
  51. /* Safe memory allocation.
  52.    Exits on error. */
  53. {
  54.    void *p;
  55.    if (n <= 0) {
  56.       fputs("mem: Invalid block size\n", stderr);
  57.       exit(1);
  58.    }
  59.    p = malloc(n);
  60.    if (!p) {
  61.       perror("mem");
  62.       exit(1);
  63.    }
  64.    return p;
  65. }
  66.  
  67. int stricmp(const char *s1, const char *s2)
  68. /* Case-insensitive string comparison via conversion to upper case.
  69.    Return -1 if s1 is alphabetically before s2, 1 if after, 0 if equal. */
  70. {
  71.    int i, c1, c2;
  72.    i = 0;
  73.    do {
  74.       c1 = toupper(s1[i]);
  75.       c2 = toupper(s2[i]);
  76.       i++;
  77.       if (c1 < c2) return -1;
  78.       if (c1 > c2) return 1;
  79.    } while (c1);
  80.    return 0;
  81. }
  82.  
  83. ulong popbitq(ulong *buf, int *bsize, int nbits)
  84. /* Returns the lowest nbits bits of *buf.
  85.    Removes those bits from *buf and adjusts *bsize appropriately. */
  86. {
  87.    ulong b;
  88.    b = *buf & lowbits(nbits);
  89.    *buf >>= nbits;
  90.    if ((*bsize -= nbits) < 0) {
  91.       fputs("tasc: Bit buffer underflow\n", stderr);
  92.       exit(1);
  93.    }
  94.    return b;
  95. }
  96.  
  97. ulong pushbitq(ulong *buf, int *bsize, int nbits, ulong bits)
  98. /* Pushes the low nbits of bits onto the high end of *buf.
  99.    Adjusts *bsize appropriately.
  100.    Returns the bits actually pushed. */
  101. {
  102.    bits &= lowbits(nbits);
  103.    *buf |= bits << *bsize;
  104.    if ((*bsize += nbits) > sizeof *buf * CHAR_BIT) {
  105.       fputs("tasc: Bit buffer overflow\n", stderr);
  106.       exit(1);
  107.    }
  108.    return bits;
  109. }
  110.  
  111. int asctobin(FILE *fasc, FILE *fbin)
  112. /* Translate ASC to HP48 binary.
  113.    Returns 0 if ok, 1 on error. */
  114. {
  115.    ulong buf=0, crc=0;
  116.    long nibs;
  117.    int bsize=0, c, d;
  118.    if (verb) fputs("Mode: ASC->bin\n", stderr);
  119.  
  120.    /* scan input file to find line beginning with '"' */
  121.    c = '\n';
  122.    do {
  123.       d = c;
  124.       c = getc(fasc);
  125.    } while (c != EOF && (d != '\n' || c != '"'));
  126.    if (c == EOF) {
  127.       fputs("ASC->bin: Invalid ASC file\n", stderr);
  128.       return 1;
  129.    }
  130.  
  131.    /* write header into binary file */
  132.    fputs("HPHP48-", fbin);
  133.    putc(rom, fbin);
  134.    if (verb)
  135.       fprintf(stderr, ROMFMT, rom);
  136.  
  137.    /* translate data */
  138.    nibs = -4;
  139.    while (fscanf(fasc, "%1x", &d) == 1) {
  140.       pushbitq(&buf, &bsize, 4, d);
  141.       calc_crc(crc, d);
  142.       nibs++;
  143.       if (bsize >= 16 + CHAR_BIT)
  144.           fputc((int) popbitq(&buf, &bsize, CHAR_BIT), fbin);
  145.    }
  146.  
  147.    /* check CRC */
  148.    if (bsize > 16)
  149.       fputc((int) popbitq(&buf, &bsize, bsize - 16), fbin);
  150.    if (crc || bsize != 16) {
  151.       fputs("ASC->bin: CRC failed\n", stderr);
  152.       return 1;
  153.    }
  154.    if (verb)
  155.       fprintf(stderr, BYTESFMT, buf, nibs/2, nibs&1?HALFFMT:"");
  156.    return 0;
  157. }
  158.  
  159. int bintoasc(FILE *fbin, FILE *fasc)
  160. /* Translates HP48 binary to ASC format.
  161.    Return 0 if ok, 1 on error. */
  162. {
  163.    ulong buf=0, crc=0, skip=0;
  164.    long nibs;
  165.    int bsize=0, c, width=0;
  166.    enum { NONE, SIZE, ASCIC, ASCIX, DIR } state=NONE;
  167.    const int MAXWIDTH=64;
  168.    char str[7];
  169.    if (verb) fputs("Mode: bin->ASC\n", stderr);
  170.  
  171.    /* check input for "HPHP48-" header */
  172.    if (fread(str, 1, 7, fbin) != 7
  173.       || strncmp(str, "HPHP48-", 7)
  174.       || (c = getc(fbin)) == EOF) {
  175.       fputs("bin->ASC: Invalid source file header\n", stderr);
  176.       return 1;
  177.    }
  178.    if (verb)
  179.       fprintf(stderr, ROMFMT, c);
  180.  
  181.    /* write header into ASC file */
  182.    fprintf(fasc, "%%%%HP: T(1)A(R)F(.); @ tasc v%s file\n\"", VERSION);
  183.  
  184.    nibs = 0;
  185.    while ((c = getc(fbin)) != EOF) {
  186.       pushbitq(&buf, &bsize, CHAR_BIT, c);
  187.  
  188.       /* parse input HP objects */
  189.       if (!skip)
  190.           switch (state) {
  191.              case NONE: if (bsize >= 20) {
  192.                 ulong pro = buf & lowbits(20);
  193.                 skip = 5;
  194.                 if (pro == 0x29e8uL || pro == 0x2a0auL || pro == 0x2a2cuL
  195.                      || pro == 0x2a4euL || pro == 0x2b1euL || pro == 0x2b40uL
  196.                      || pro == 0x2b62uL || pro == 0x2b88uL || pro == 0x2dccuL)
  197.                      state = SIZE;
  198.                 else if (pro == 0x2e48uL || pro == 0x2e6duL || pro == 0x2afcuL)
  199.                      state = ASCIC;
  200.                 else if (pro == 0x2a96uL) state = DIR, skip = 8;
  201.                 else if (pro == 0x2911uL) skip = 10;
  202.                 else if (pro == 0x2933uL) skip = 21;
  203.                 else if (pro == 0x2955uL) skip = 26;
  204.                 else if (pro == 0x2977uL) skip = 37;
  205.                 else if (pro == 0x299duL) skip = 47;
  206.                 else if (pro == 0x29bfuL) skip = 7;
  207.                 else if (pro == 0x2e92uL) skip = 11;
  208.              }
  209.              break;
  210.              case SIZE: if (bsize >= 20)
  211.                 state = NONE, skip = buf & lowbits(20);
  212.              break;
  213.              case ASCIC: if (bsize >= 8)
  214.                 state = NONE, skip = 2 + 2 * (buf & lowbits(8));
  215.              break;
  216.              case ASCIX: if (bsize >= 8)
  217.                 state = NONE, skip = 4 + 2 * (buf & lowbits(8));
  218.              break;
  219.              case DIR: if (bsize >= 20)
  220.                 state = ASCIX, skip = buf & lowbits(20);
  221.              break;
  222.           }
  223.  
  224.       /* write already interpreted binary data */
  225.       while (skip && bsize >= 4) {
  226.           c = (int) popbitq(&buf, &bsize, 4);
  227.           if (width == MAXWIDTH) {
  228.              putc('\n', fasc);
  229.              width = 0;
  230.           }
  231.           fprintf(fasc, "%1.1X", c);
  232.           width++;
  233.           calc_crc(crc, c);
  234.           skip--;
  235.           nibs++;
  236.       }
  237.    }
  238.    if (buf) {
  239.       fprintf(stderr, "bin->ASC: Binary parsed incorrectly\n");
  240.       return 1;
  241.    }
  242.  
  243.    /* append CRC */
  244.    buf = crc;
  245.    bsize = 16;
  246.    while (bsize) {
  247.       if (width == MAXWIDTH) {
  248.           putc('\n', fasc);
  249.           width = 0;
  250.       }
  251.       fprintf(fasc, "%1.1X", (int) popbitq(&buf, &bsize, 4));
  252.       width++;
  253.    }
  254.    fputs("\"\n@ ", fasc);
  255.    fprintf(fasc, BYTESFMT, crc, nibs/2, nibs&1?HALFFMT:"");
  256.    if (verb)
  257.       fprintf(stderr, BYTESFMT, crc, nibs/2, nibs&1?HALFFMT:"");
  258.    return 0;
  259. }
  260.  
  261. int translate(const char *fsrc, const char *fdest, int mode)
  262. /* Translate file named fsrc to file named fdest, using mode mode.
  263.    fsrc == NULL means use stdin; fdest == NULL means use stdout.
  264.    Return 0 if ok, 1 on error. */
  265. {
  266.    FILE *in, *out;
  267.    int i;
  268.    switch (mode) {
  269.       case ATOB: if (fsrc) {
  270.           in = fopen(fsrc, "r");
  271.           if (!in) { perror(fsrc); return 1; }
  272.       } else
  273.           in = stdin;
  274.       if (fdest) {
  275.           out = fopen(fdest, "wb");
  276.           if (!out) { perror(fdest); return 1; }
  277.       } else {
  278.           fputs("ASC->bin: Case not implemented\n",stderr);
  279.           exit(1);
  280.       }
  281.       i = asctobin(in, out);
  282.       break;
  283.       case BTOA: if (fsrc) {
  284.           in = fopen(fsrc, "rb");
  285.           if (!in) { perror(fsrc); return 1; }
  286.       } else {
  287.           fputs("bin->ASC: Case not implemented\n",stderr);
  288.           exit(1);
  289.       }
  290.       if (fdest) {
  291.           out = fopen(fdest, "w");
  292.           if (!out) { perror(fdest); return 1; }
  293.       } else
  294.           out = stdout;
  295.       i = bintoasc(in, out);
  296.       break;
  297.       default: fputs("tasc: Unimplemented translation mode\n", stderr);
  298.       exit(1);
  299.    }
  300.    fclose(in);
  301.    fclose(out);
  302.    return i;
  303. }
  304.  
  305. int main(int argc, char **argv, char **envp)
  306. /* Uses:
  307.    tasc options files
  308.    options:
  309.    -d Force ASC->bin translation (decode ASC)
  310.    -e Force bin->ASC translation (encode ASC)
  311.    -i Use stdin for -a mode / stdout for -b mode:
  312.       ignored in auto-translation
  313.    -q Suppress printouts (quiet)
  314.    -r<let> Set rom revision <let>:
  315.       ignored in bin->ASC translation
  316.    files:
  317.    names of input/output files
  318.    return code: 0 ok, 1 error */
  319. {
  320.    int e, mode=0, stdio=0, argi;
  321.    char *p, *q;
  322.  
  323.    /* interpret options as described above */
  324.    e = 0;
  325.    for (argi = 1; argv[argi] && argv[argi][0] == '-'; argi++)
  326.       for (p = argv[argi] + 1; *p; p++) {
  327.           switch (*p) {
  328.              case 'd': if (mode) e++; else mode = ATOB; break;
  329.              case 'e': if (mode) e++; else mode = BTOA; break;
  330.              case 'i': if (stdio) e++; else stdio = 1; break;
  331.              case 'q': if (verb) verb = 0; else e++; break;
  332.              case 'r': if (p[1]) {
  333.                 p++;
  334.                 if (!strchr("ABCDEF", rom = toupper(*p))) e++;
  335.              } else e++;
  336.              break;
  337.              default: e++;
  338.           }
  339.       }
  340.    if (e) {
  341.       fprintf(stderr, "Use: %s [-deiqr<let>] file [file]\n", argv[0]);
  342.       return 1;
  343.    }
  344.    if (verb) fprintf(stderr, "TASC version %s\nCompiled on %s at %s\n",
  345.       VERSION, __DATE__, __TIME__);
  346.  
  347.    /* translate files by method specified */
  348.    switch (mode) {
  349.       case ATOB: if (stdio) {
  350.           if (argc - argi == 1) e = translate(0, argv[argi], ATOB);
  351.           else {
  352.              fputs("ASC->bin: Specify output filename\n", stderr);
  353.              return 1;
  354.           }
  355.       } else {
  356.           if (argc - argi == 2) e = translate(argv[argi], argv[argi+1], ATOB);
  357.           else {
  358.              fputs("ASC->bin: Specify input and output filenames\n", stderr);
  359.              return 1;
  360.           }
  361.       }
  362.       break;
  363.       case BTOA: if (stdio) {
  364.           if (argc - argi == 1) e = translate(argv[argi], 0, BTOA);
  365.           else {
  366.              fputs("bin->ASC: Specify input filename\n", stderr);
  367.              return 1;
  368.           }
  369.       } else {
  370.           if (argc - argi == 2) e = translate(argv[argi], argv[argi+1], BTOA);
  371.           else {
  372.              fputs("bin->ASC: Specify input and output filenames\n", stderr);
  373.              return 1;
  374.           }
  375.       }
  376.       break;
  377.       case 0: if (argc - argi == 1) {
  378.           int n;
  379.           n = strlen(argv[argi]);
  380.           q = alloc(n + sizeof ASCSUF);
  381.           strcpy(q, argv[argi]);
  382.           n -= sizeof ASCSUF - 1;
  383.           if (n > 0 && !stricmp(q+n, ASCSUF)) {
  384.              q[n] = 0;
  385.              e = translate(argv[argi], q, ATOB);
  386.           } else {
  387.              p = strrchr(q, ASCSUF[0]);
  388.              if (p) strcpy(p, ASCSUF); else strcat(q, ASCSUF);
  389.              e = translate(argv[argi], q, BTOA);
  390.           }
  391.           free(q);
  392.       } else if (argc - argi == 2) {
  393.           e = translate(argv[argi], argv[argi+1],
  394.              stricmp(argv[argi]+strlen(argv[argi])-(sizeof ASCSUF -1), ASCSUF)
  395.              ? BTOA : ATOB);
  396.       } else {
  397.           fputs("tasc: Specify source [and target] filename[s]\n", stderr);
  398.           return 1;
  399.       }
  400.       break;
  401.    }
  402.    return e;
  403. }
  404.